﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Borg.Framework
{
    public class ExtensionTypeComboBox : ComboBox
    {
        private ExtensionTypeInfo _currentType;
        private SlotInfo _slotInfo;

        private void Add(PlugInfo pi)
        {
            var extObj = pi.ExtensionInfo.Object is IObjectInfo
                ? pi.ExtensionInfo.Object
                : pi.ExtensionInfo;
            
            Items.Add(pi, extObj);
        }

        public void Attach(SlotInfo si)
        {
            if (_slotInfo != si && _slotInfo != null)
                Detach(_slotInfo);

            _slotInfo = si;

            si.Plugged += Plugged;
            si.Unplugged += Unplugged;
        }

        public void Detach(SlotInfo si)
        {
            if (_slotInfo == si)
            {
                _slotInfo = null;
                Items.Clear();
            }

            si.Plugged -= Plugged;
            si.Unplugged -= Unplugged;

            if (_currentType == null)
                return;

            _currentType.ExtensionCreated -= ExtensionCreated;
            _currentType.ExtensionReleased -= ExtensionReleased;
        }

        public void ExtensionCreated(object sender, ExtensionEventArgs args)
        {
            var pi = args.ExtensionInfo.PlugInfos[_slotInfo.Name];
            Add(pi);

            if (SelectedIndex == 0)
                _slotInfo.Plug(pi);
        }

        public void ExtensionReleased(object sender, ExtensionEventArgs args)
        {
            if (_slotInfo != null)
                Remove(args.ExtensionInfo.PlugInfos[_slotInfo.Name]);
        }

        protected override void OnSelectedIndexChanged(EventArgs e)
        {
            base.OnSelectedIndexChanged(e);

            if (noUpdate || SelectedIndex < 0)
                return;

            int i;
            PlugInfo key;
            KeyValuePair<object, object> pair;

            switch (SelectedIndex)
            {
                case 0:
                    for (i = 1; i < Items.Count; i++)
                    {
                        pair = Items[i];
                        key = (PlugInfo)pair.Key;

                        if (!_slotInfo.PluggedPlugInfos.Contains(key))
                            _slotInfo.Plug(key);
                    }
                    break;
                
                default:
                    if (!_slotInfo.PluggedPlugInfos.Contains((PlugInfo) SelectedKey))
                        _slotInfo.Plug((PlugInfo) SelectedKey);
                    for (i = 1; i < Items.Count; i++)
                    {
                        pair = Items[i];
                        key = (PlugInfo)pair.Key;

                        if (SelectedKey != key)
                            _slotInfo.Unplug(key);
                    }
                    break;
            }
        }

        public void Plugged(object sender, PlugEventArgs args)
        {
            if (args.PlugInfo.ExtensionInfo.ExtensionTypeInfo != _currentType)
            {
                if (_currentType != null)
                {
                    _currentType.ExtensionCreated -= ExtensionCreated;
                    _currentType.ExtensionReleased -= ExtensionReleased;
                }
                
                _currentType = args.PlugInfo.ExtensionInfo.ExtensionTypeInfo;
                
                _currentType.ExtensionCreated += ExtensionCreated;
                _currentType.ExtensionReleased += ExtensionReleased;
                
                UnplugUnfitting();
                Items.Clear();
                Items.Add(args.PlugInfo.PlugTypeInfo, string.Format("All ({0})", _currentType.Name));

                foreach (var extension in _currentType.ExtensionInfos)
                    Add(extension.PlugInfos[_slotInfo.Name]);
            }

            var selectedIndex = -1;

            var count = _slotInfo.PluggedPlugInfos.Count(
                plug => plug.ExtensionInfo.ExtensionTypeInfo == _currentType);

            if (count == _currentType.ExtensionInfos.Count)
                selectedIndex = 0;

            else if (count == 1)
            {
                for (var i = 1; i < Items.Count; i++)
                {
                    var pair = Items[i];

                    if (pair.Key != args.PlugInfo)
                        continue;

                    selectedIndex = i;
                    break;
                }
            }

            noUpdate = true;
            SelectedIndex = selectedIndex;
            noUpdate = false;
        }

        private void Remove(PlugInfo pi)
        {
            Items.Remove(pi);
        }

        public void Unplugged(object sender, PlugEventArgs args)
        {
            if (_slotInfo.PluggedPlugInfos.Count == 0)
            {
                if (_currentType != null)
                {
                    _currentType.ExtensionCreated -= ExtensionCreated;
                    _currentType.ExtensionReleased -= ExtensionReleased;
                }
                
                _currentType = null;
                SelectedIndex = -1;
                Items.Clear();
            }

            else if (args.PlugInfo.ExtensionInfo.ExtensionTypeInfo == _currentType)
            {
                var selectedIndex = -1;

                if (_slotInfo.PluggedPlugInfos.Count == 1)
                {
                    var plug = _slotInfo.PluggedPlugInfos[0];

                    for (var i = 1; i < Items.Count; i++)
                    {
                        var pair = Items[i];

                        if (pair.Key != plug)
                            continue;

                        selectedIndex = i;
                        break;
                    }
                }

                noUpdate = true;
                SelectedIndex = selectedIndex;
                noUpdate = false;
            }
        }

        public void UnplugUnfitting()
        {
            for (var i = _slotInfo.PluggedPlugInfos.Count - 1; i >= 0; i--)
                if (_slotInfo.PluggedPlugInfos[i].ExtensionInfo.ExtensionTypeInfo != _currentType)
                    _slotInfo.Unplug(_slotInfo.PluggedPlugInfos[i]);
        }

        public ExtensionTypeInfo CurrentType
        {
            get { return _currentType; }
        }
    }
}

